package com.fushenlan.epidemicControl.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fushenlan.Vo.AppUserVo;
import com.fushenlan.Vo.CertificateInfoVo;
import com.fushenlan.commons.ApiGlobalErrorDefinition;
import com.fushenlan.commons.CheckParamUtil;
import com.fushenlan.commons.GeneralResponse;
import com.fushenlan.enums.IdentityTypeEnum;
import com.fushenlan.epidemicControl.config.JwtProvider;
import com.fushenlan.epidemicControl.dto.AddOrUpdateUserReqDTO;
import com.fushenlan.epidemicControl.dto.QueryUserReqDTo;
import com.fushenlan.epidemicControl.service.UserService;
import com.fushenlan.epidemicControl.sms.SmsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private SmsUtil smsUtil;

    public static final String REDIS_PHONE_KEY = "phone:";

    @Autowired
    private JwtProvider jwtProvider;

    @RequestMapping("/login")
    public GeneralResponse<Map<String, Object>> login(@RequestBody AppUserVo appUserVo) {
        if (StrUtil.isBlank(appUserVo.getOpenid())) {
            log.info("openid为空");
            return GeneralResponse.fail("-1", "登录失败", "");
        }

        //居民方式登录
        CertificateInfoVo myInfo = appUserVo.getMyInfo();
        if (1 == appUserVo.getRoleId()) {
            //验证参数必填
            String s = CheckParamUtil.checkNull(appUserVo, Arrays.asList("streetCode", "neighborhoodId", "residentialId", "buildingId", "houseId", "familyList"));
            if (StrUtil.isNotBlank(s)) {
                return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, s);
            }
            //校验本人
            String checkResult = CheckParamUtil.checkNull(myInfo, Arrays.asList("userName", "identifyType", "identifyNo", "phoneNumber", "verificationCode"));
            if (StrUtil.isNotBlank(checkResult)) {
                return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, checkResult);
            }
            //证件号合法性
            String val = checkCertificateValid(myInfo.getIdentifyType(), myInfo.getIdentifyNo());
            if (StrUtil.isNotBlank(val)) {
                return GeneralResponse.fail("9011", val, "");
            }
            //校验家庭成员
            List<CertificateInfoVo> familyList = appUserVo.getFamilyList();
            for (CertificateInfoVo certificateInfoVo : familyList) {
                String res = CheckParamUtil.checkNull(certificateInfoVo, Arrays.asList("userName", "identifyType", "identifyNo"));
                if (StrUtil.isNotBlank(res)) {
                    return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, res);
                }
                //证件号合法性
                String valid = checkCertificateValid(certificateInfoVo.getIdentifyType(), certificateInfoVo.getIdentifyNo());
                if (StrUtil.isNotBlank(valid)) {
                    return GeneralResponse.fail("9011", valid, "");
                }
            }
            //验证手机验证码
            String result = checkVerificationCode(myInfo.getPhoneNumber(), myInfo.getVerificationCode());
            if (StrUtil.isNotBlank(result)) {
                log.info("手机验证错误:{}", result);
                return GeneralResponse.fail(ApiGlobalErrorDefinition.SHORTMSG_INVALID, result);
            }

            //插入
            Map<String, Object> objectMap = userService.insert(appUserVo);
            //在这里下发token
            Map<String, Object> payload = new HashMap<>();
            payload.put("openid", appUserVo.getOpenid());
            payload.put("userId", objectMap.get("userId"));
            payload.put("userName", objectMap.get("userName"));
            payload.put("roleId", objectMap.get("roleId"));
            String token = jwtProvider.createToken(payload, appUserVo.getOpenid());
            objectMap.put("access_token", token);
            objectMap.put("registerStatus", "1");
            return GeneralResponse.success(objectMap);
        }

        //居委会方式登录
        if (2 == appUserVo.getRoleId()) {
            String s = CheckParamUtil.checkNull(myInfo, Arrays.asList("phoneNumber", "verificationCode"));
            if (StrUtil.isNotBlank(s)) {
                return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, s);
            }
            //验证手机验证码
            String result = checkVerificationCode(myInfo.getPhoneNumber(), myInfo.getVerificationCode());
            if (StrUtil.isNotBlank(result)) {
                log.info("手机验证错误:{}", result);
                return GeneralResponse.fail(ApiGlobalErrorDefinition.SHORTMSG_INVALID, result);
            }
            //查询后台居委会成员数据
            Map<String, Object> map = userService.selectNeighborhood(myInfo.getPhoneNumber());
            log.info("手机号查询居委成员结果：{}", JSONUtil.toJsonStr(map));
            if (CollUtil.isEmpty(map)) {
                log.info("您不是居委会成员，请确认信息后重新登录");
                return GeneralResponse.fail("-1", "您不是居委会成员，请确认信息后重新登录", "");
            }
            //手机号脱敏处理
            map.put("memberPhone", map.get("memberPhone").toString().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
            map.put("roleId", appUserVo.getRoleId());
            map.put("switchRole", 1);
            //在这里下发token
            Map<String, Object> payload = new HashMap<>();
            payload.put("openid", appUserVo.getOpenid());
            payload.put("userId", map.get("inhabitantId"));
            payload.put("userName", map.get("userName"));
            payload.put("roleId", map.get("roleId"));
            String token = jwtProvider.createToken(payload, appUserVo.getOpenid());
            map.put("registerStatus", "1");
            map.put("access_token", token);
            return GeneralResponse.success(map);
        }
        return GeneralResponse.fail();
    }

    /**
     * 获取手机验证码
     *
     * @param phoneNumber 手机号
     * @return 手机验证码
     */
    @GetMapping("/getVerificationCode/{phoneNumber}")
    public GeneralResponse<?> getVerificationCode(@PathVariable("phoneNumber") String phoneNumber) {
        if (StrUtil.isBlank(phoneNumber)) {
            return GeneralResponse.fail("-1", "请输入手机号", "");
        }
        if (!PhoneUtil.isMobile(phoneNumber)) {
            return GeneralResponse.fail("-1", "手机号不合法", "");
        }
        //生成六位随机数
        String code = RandomUtil.randomNumbers(6);
        log.info("验证码：{}", code);
        //为防止一分钟内重复发送短信，增加限制标志send_flag_mobile
        String flag = "send_flag_mobile:" + phoneNumber;
        String s = redisTemplate.opsForValue().get(flag);
        log.info("redis中发送手机验证码重复标志：{} value:{}", flag, s);
        if (StrUtil.isNotBlank(s)) {
            return GeneralResponse.fail("-1", "请勿重复发送", "");
        }
        try {
            //发送短信
            Map<String, String> respMap = smsUtil.sendMessage(phoneNumber, code);
            log.info("短信发送结果：{}", JSONUtil.toJsonStr(respMap));
            if (!"ACCEPTD".equals(respMap.get("mtstat")) || !"000".equals(respMap.get("mterrcode"))) {
                log.info("短信验证码发送失败");
                return GeneralResponse.fail("-1", "短信验证码发送失败", "");
            }
            log.info("验证码已成功发送！");
        } catch (Exception e) {
            log.error("短信验证码发送失败", e);
        }

        //验证码存放redis中，重新写入发送标志
        redisTemplate.executePipelined((RedisCallback<?>) redisConnection -> {
            redisConnection.openPipeline();
            //1分钟内不能重复发送
            redisTemplate.opsForValue().set(flag, UUID.randomUUID().toString().replaceAll("-", ""), 1L, TimeUnit.MINUTES);
            //验证码3分钟有效期
            redisTemplate.opsForValue().set(REDIS_PHONE_KEY + phoneNumber, code, 3L, TimeUnit.MINUTES);
            redisConnection.closePipeline();
            return null;
        });
        return GeneralResponse.success();
    }

    /**
     * 查询居民的信息
     *
     * @param reqDTo
     * @return
     */
    @RequestMapping("/queryMyInfo")
    public GeneralResponse<Map<String, Object>> queryMyInfo(@RequestBody QueryUserReqDTo reqDTo) {
        String aNull = CheckParamUtil.checkNull(reqDTo, Arrays.asList("inhabitantId"));
        if (StrUtil.isNotBlank(aNull)) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, aNull);
        }
        Map<String, Object> map = userService.selectAllById(reqDTo);
        log.info("db查询用户信息结果：{}", JSONUtil.toJsonStr(map));
        if (CollUtil.isEmpty(map)) {
            log.info("未查询到用户信息");
            return GeneralResponse.fail(ApiGlobalErrorDefinition.USER_NOT_EXIST, "未查询到用户信息");
        }
        return GeneralResponse.success(map);
    }

    /**
     * 根据居民表主键id查询居民的信息
     *
     * @param reqDTo
     * @return
     */
    @RequestMapping("/queryUserById")
    public GeneralResponse<Map<String, Object>> queryUserById(@RequestBody QueryUserReqDTo reqDTo) {
        String aNull = CheckParamUtil.checkNull(reqDTo, Arrays.asList("inhabitantId"));
        if (StrUtil.isNotBlank(aNull)) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, aNull);
        }
        Map<String, Object> map = userService.queryUserById(reqDTo);
        if (CollUtil.isEmpty(map)) {
            log.info("未查询到用户信息");
            return GeneralResponse.fail(ApiGlobalErrorDefinition.USER_NOT_EXIST, "未查询到用户信息");
        }
        return GeneralResponse.success(map);
    }

    /**
     * 添加居民信息
     *
     * @param reqDTo
     * @return
     */
    @RequestMapping("/addUser")
    public GeneralResponse<Map<String, Object>> addUser(@RequestBody AddOrUpdateUserReqDTO reqDTo) {
        //验证参数必填
        String s = CheckParamUtil.checkNull(reqDTo, Arrays.asList("inhabitantId", "streetCode", "neighborhoodId", "residentialId", "buildingId", "houseId", "userName", "identifyType", "identifyNo", "phoneNumber"));
        if (StrUtil.isNotBlank(s)) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, s);
        }
        String certificateValid = checkCertificateValid(reqDTo.getIdentifyType(), reqDTo.getIdentifyNo());
        if (StrUtil.isNotBlank(certificateValid)) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, certificateValid);
        }
        Map<String, Object> respMap = null;
        try {
            respMap = userService.addUser(reqDTo);
        } catch (Exception e) {
            log.error("添加失败", e);
            return GeneralResponse.fail("-1", e.getMessage(), "");
        }
        return GeneralResponse.success(respMap);
    }

    /**
     * 添加居民信息
     *
     * @param reqDTo
     * @return
     */
    @RequestMapping("/updateUser")
    public GeneralResponse<Map<String, Object>> updateUser(@RequestBody AddOrUpdateUserReqDTO reqDTo) {
        //验证参数必填
        if (reqDTo.getFamilyId() == 0) {
            //我自己
            String s = CheckParamUtil.checkNull(reqDTo, Arrays.asList("inhabitantId", "streetCode", "neighborhoodId", "residentialId", "buildingId", "houseId", "userName", "identifyType", "identifyNo", "phoneNumber"));
            if (StrUtil.isNotBlank(s)) {
                return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, s);
            }
        } else {
            //家庭成员
            String s = CheckParamUtil.checkNull(reqDTo, Arrays.asList("inhabitantId", "userName", "identifyType", "identifyNo", "phoneNumber"));
            if (StrUtil.isNotBlank(s)) {
                return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, s);
            }
        }
        String certificateValid = checkCertificateValid(reqDTo.getIdentifyType(), reqDTo.getIdentifyNo());
        if (StrUtil.isNotBlank(certificateValid)) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, certificateValid);
        }
        Map<String, Object> respMap = null;
        try {
            respMap = userService.updateUser(reqDTo);
        } catch (Exception e) {
            log.error("修改失败", e);
            return GeneralResponse.fail("-1", e.getMessage(), "修改失败");
        }
        return GeneralResponse.success(respMap);
    }

    /**
     * 查询居委会成员信息
     *
     * @param reqDTo
     * @return
     */
    @RequestMapping("/queryNeighborhoodMemberById")
    public GeneralResponse<Map<String, Object>> queryNeighborhoodMember(@RequestBody QueryUserReqDTo reqDTo) {
        String aNull = CheckParamUtil.checkNull(reqDTo, Arrays.asList("neighborhoodMemberId"));
        if (StrUtil.isNotBlank(aNull)) {
            return GeneralResponse.fail(ApiGlobalErrorDefinition.ERROR_REQUEST_BLANK, aNull);
        }
        Map<String, Object> map = userService.queryNeighborhoodMemberById(reqDTo);
        if (CollUtil.isEmpty(map)) {
            log.info("未查询到居委成员信息");
            return GeneralResponse.fail(ApiGlobalErrorDefinition.USER_NOT_EXIST, "未查询到居委成员信息");
        }
        return GeneralResponse.success(map);
    }

    /**
     * 校验证件信息
     *
     * @param identifyType 证件类型
     * @param identifyNo   证件号
     * @return
     */
    private String checkCertificateValid(Integer identifyType, String identifyNo) {
        //身份证号码合法性验证
        if (IdentityTypeEnum.certificate.getType().equals(identifyType)) {
            if (!IdcardUtil.isValidCard(identifyNo)) {
                return "证件号码【" + identifyNo + "】不合法";
            }
        }
        return null;
    }

    /**
     * 验证手机验证码
     *
     * @param phoneNumber      手机号
     * @param verificationCode 入参验证码
     * @return
     */
    private String checkVerificationCode(String phoneNumber, String verificationCode) {
        if (!PhoneUtil.isMobile(phoneNumber)) {
            log.info("手机号码不合法:{}", phoneNumber);
            return "请输入正确的手机号码";
        }
        //获取redis中的手机验证码
        String redisVerifiCode = redisTemplate.opsForValue().get(REDIS_PHONE_KEY + phoneNumber);
        log.info("key:{} redis存储验证码：{} 前端入参验证码：{}", REDIS_PHONE_KEY + phoneNumber, redisVerifiCode, verificationCode);
        if (StrUtil.isBlank(redisVerifiCode)) {
            log.info("未查询到redis验证码");
            return "请重新获取验证码";
        }
        if (!redisVerifiCode.equals(verificationCode)) {
            log.info("验证码不正确");
            return "验证码不正确";
        }
        return null;
    }
}